#include "Common.fx"
#include "Sampling.fx"

//--------------------------------------
struct a2v {
	a2vApplyBasic basic;
};

struct v2f {
	v2fApplyBasic basic;
};

//--------------------------------------
sampler ProjLightMapSamp : register(s2);
sampler ShadowMapSamp : register(s3);

//--------------------------------------
v2f vsMain(a2v input) {
	
	v2f output;
	
	ApplyBasicVertexProcess(output.basic, input.basic);
	
	return output;
}

//--------------------------------------
void ComputeShadowTerm2x2(
	out float shadowTerm,
	in float2 inSmTexcoord,
	in float2 inLerpWeight,
	in float4 inLsPosZ) {

	float4 smVals;
	float4 smResults;
	
	for(int i=0; i<4; ++i) {
	
		FetchOffsetedSample( smVals[i], ShadowMapSamp, inSmTexcoord, g_Uniform2x2[i] );
	}
		
	smResults = inLsPosZ < smVals;
	
	shadowTerm = lerp(
		lerp(smResults[0], smResults[1], inLerpWeight.x),
		lerp(smResults[2], smResults[3], inLerpWeight.x),
		inLerpWeight.y);
	
}

//--------------------------------------
void ComputeShadowTerm3x3(
	out float shadowTerm,
	in float2 inSmTexcoord,
	in float2 inLerpWeight,
	in float4 inLsPosZ) {

	float3 smVals[3];
	float3 smResults[3];
	
	for(int i=0; i<3; ++i)
	{
		for(int j=0; j<3; ++j)
		{
			FetchOffsetedSample( smVals[i][j], ShadowMapSamp, inSmTexcoord, g_Uniform3x3[i * 3 + j] );
		}
		
		// compare 3 samples
		smResults[i] = inLsPosZ < smVals[i];
	}
	
	BilinearInterpolateResults3x3(shadowTerm, smResults, inLerpWeight);
	
}

//--------------------------------------
void ComputeShadowTerm4x4(
	out float shadowTerm,
	in float2 inSmTexcoord,
	in float2 inLerpWeight,
	in float4 inLsPosZ) {

	float4 smVals[4];
	float4 smResults[4];
	
	for(int i=0; i<4; ++i)
	{
		for(int j=0; j<4; ++j)
		{
			FetchOffsetedSample( smVals[i][j], ShadowMapSamp, inSmTexcoord, g_Uniform4x4[i * 4 + j] );
		}
		
		// compare 4 samples
		smResults[i] = inLsPosZ < smVals[i];
	}
	
	BilinearInterpolateResults4x4(shadowTerm, smResults, inLerpWeight);
	
}

//--------------------------------------
void ComputeShadowTerm8x8(
	out float shadowTerm,
	in float2 inSmTexcoord,
	in float2 inLerpWeight,
	in float4 inLsPosZ) {

	float4 smVals[16];
	float4 smResults[16];
	
	for(int i=0; i<8; ++i)
	{
		int idx = i * 2;
		
		FetchOffsetedSample( smVals[idx][0], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 0] );
		FetchOffsetedSample( smVals[idx][1], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 1] );
		FetchOffsetedSample( smVals[idx][2], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 2] );
		FetchOffsetedSample( smVals[idx][3], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 3] );
		
		FetchOffsetedSample( smVals[idx+1][0], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 4] );
		FetchOffsetedSample( smVals[idx+1][1], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 5] );
		FetchOffsetedSample( smVals[idx+1][2], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 6] );
		FetchOffsetedSample( smVals[idx+1][3], ShadowMapSamp, inSmTexcoord, g_Uniform8x8[i * 8 + 7] );
		
		// compare 4 samples
		smResults[idx] = inLsPosZ < smVals[idx];
		smResults[idx+1] = inLsPosZ < smVals[idx+1];
	}
	
	BilinearInterpolateResults8x8(shadowTerm, smResults, inLerpWeight);
	
}

//--------------------------------------
f2fb psMain2x2(v2f input) {

	f2fb output;
	
	input.basic.lsPos.xy /= input.basic.lsPos.w;
	input.basic.lsPos.xy = saturate(input.basic.lsPos.xy);
	
	float4 lightMap = tex2D(ProjLightMapSamp, input.basic.lsPos.xy);
	
	if(input.basic.lsPos.z < 0.01)
		lightMap = 0;
	
	float2 smTexcoord;
	ShadowMapBaseTexcoord(smTexcoord, input.basic.lsPos.xy);
	
	float2 lerpWeight = frac(smTexcoord * g_ShadowMapSize);
	
	float shadowTerm;
	
	ComputeShadowTerm2x2(shadowTerm, smTexcoord, lerpWeight, input.basic.lsPos.z);
	
	lightMap *= shadowTerm;
	
	ApplyBasicFragmentProcess(output.c0, input.basic, lightMap );
	
	return output;
}

//--------------------------------------
f2fb psMain3x3(v2f input) {

	f2fb output;
	
	input.basic.lsPos.xy /= input.basic.lsPos.w;
	input.basic.lsPos.xy = saturate(input.basic.lsPos.xy);
	
	float4 lightMap = tex2D(ProjLightMapSamp, input.basic.lsPos.xy);
	
	if(input.basic.lsPos.z < 0.01)
		lightMap = 0;
	
	float2 smTexcoord;
	ShadowMapBaseTexcoord(smTexcoord, input.basic.lsPos.xy);
	
	float2 lerpWeight = frac(smTexcoord * g_ShadowMapSize);
	
	float shadowTerm;
	
	ComputeShadowTerm3x3(shadowTerm, smTexcoord, lerpWeight, input.basic.lsPos.z);
	
	lightMap *= shadowTerm;
	
	ApplyBasicFragmentProcess(output.c0, input.basic, lightMap );
	
	return output;
}


//--------------------------------------
f2fb psMain4x4(v2f input) {

	f2fb output;
	
	input.basic.lsPos.xy /= input.basic.lsPos.w;
	input.basic.lsPos.xy = saturate(input.basic.lsPos.xy);
	
	float4 lightMap = tex2D(ProjLightMapSamp, input.basic.lsPos.xy);
	
	if(input.basic.lsPos.z < 0.01)
		lightMap = 0;
	
	float2 smTexcoord;
	ShadowMapBaseTexcoord(smTexcoord, input.basic.lsPos.xy);
	
	float2 lerpWeight = frac(smTexcoord * g_ShadowMapSize);
	
	float shadowTerm;
	
	ComputeShadowTerm4x4(shadowTerm, smTexcoord, lerpWeight, input.basic.lsPos.z);
	
	lightMap *= shadowTerm;
	
	ApplyBasicFragmentProcess(output.c0, input.basic, lightMap );
	
	return output;
}

//--------------------------------------
f2fb psMain8x8(v2f input) {

	f2fb output;
	
	input.basic.lsPos.xy /= input.basic.lsPos.w;
	input.basic.lsPos.xy = saturate(input.basic.lsPos.xy);
	
	float4 lightMap = tex2D(ProjLightMapSamp, input.basic.lsPos.xy);
	
	if(input.basic.lsPos.z < 0.01)
		lightMap = 0;
	
	float2 smTexcoord;
	ShadowMapBaseTexcoord(smTexcoord, input.basic.lsPos.xy);
	
	float2 lerpWeight = frac(smTexcoord * g_ShadowMapSize);
	
	float shadowTerm;
	
	ComputeShadowTerm8x8(shadowTerm, smTexcoord, lerpWeight, input.basic.lsPos.z);
	
	lightMap *= shadowTerm;
	
	ApplyBasicFragmentProcess(output.c0, input.basic, lightMap );
	
	return output;
}

//--------------------------------------
technique Apply2x2 {

	pass {
	
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		
		VertexShader		= compile vs_3_0 vsMain();
		PixelShader			= compile ps_3_0 psMain2x2();
	}
}

//--------------------------------------
technique Apply3x3 {

	pass {
	
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		
		VertexShader		= compile vs_3_0 vsMain();
		PixelShader			= compile ps_3_0 psMain3x3();
	}
}

//--------------------------------------
technique Apply4x4 {

	pass {
	
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		
		VertexShader		= compile vs_3_0 vsMain();
		PixelShader			= compile ps_3_0 psMain4x4();
	}
}

//--------------------------------------
technique Apply8x8 {

	pass {
	
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		
		VertexShader		= compile vs_3_0 vsMain();
		PixelShader			= compile ps_3_0 psMain8x8();
	}
}


//--------------------------------------